package com.yc.testupload.crawler.strategy;

import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
import org.openqa.selenium.*;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;

import io.github.bonigarcia.wdm.WebDriverManager;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.File;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;

/**
 * 面试呀网站专用爬虫策略
 * 用于爬取JavaScript面试题网站的内容
 */
public class MianshiyaCrawlingStrategy implements CrawlingStrategy {

    private static final Logger logger = LogManager.getLogger(MianshiyaCrawlingStrategy.class);

    private static final String[] MIANSHIYA_DOMAINS = { "javascriptcn.com" };

    @Override
    public boolean isApplicable(String url) {
        if (url == null || url.trim().isEmpty())
            return false;
        for (String domain : MIANSHIYA_DOMAINS) {
            if (url.contains(domain)) {
                logger.info("URL {} 适用于面试呀爬虫策略", url);
                return true;
            }
        }
        return false;
    }

    /**
     * 动态检测 Chrome 可执行路径
     */
    private String detectChromePath() {
        // 根据不同操作系统检测Chrome路径
        String[] possiblePaths;
        
        // Windows系统路径
        if (System.getProperty("os.name").toLowerCase().contains("win")) {
            possiblePaths = new String[]{
                "C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe",
                "C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe",
                "%LOCALAPPDATA%\\Google\\Chrome\\Application\\chrome.exe",
                // 处理环境变量路径
                System.getenv("LOCALAPPDATA") + "\\Google\\Chrome\\Application\\chrome.exe"
            };
        } else {
            // Linux系统路径
            possiblePaths = new String[]{
                "/usr/bin/google-chrome",
                "/usr/bin/google-chrome-stable",
                "/opt/google/chrome/chrome",
                "/usr/local/bin/google-chrome",
                "/home/gep/soft/google-chrome"
            };
        }
        
        for (String path : possiblePaths) {
            // 处理可能包含环境变量的路径
            if (path.contains("%LOCALAPPDATA%")) {
                path = path.replace("%LOCALAPPDATA%", System.getenv("LOCALAPPDATA"));
            }
            
            File chromeFile = new File(path);
            if (chromeFile.exists() && chromeFile.canExecute()) {
                logger.info("检测到Chrome浏览器路径: {}", path);
                return path;
            }
        }
        logger.info("未检测到Chrome浏览器路径，使用默认配置");
        return null;
    }

    /**
     * 抓取列表页中所有题目链接
     */
    public List<String> extractQuestionUrls(String listPageUrl) {
        WebDriver driver = null;
        List<String> questionUrls = new ArrayList<>();
        try {
            WebDriverManager.chromedriver().setup();
            ChromeOptions options = new ChromeOptions();
            String chromePath = detectChromePath();
            if (chromePath != null)
                options.setBinary(chromePath);
            options.addArguments("--headless", "--no-sandbox", "--disable-dev-shm-usage");

            driver = new ChromeDriver(options);
            driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(10));
            // 设置页面加载超时时间为60秒以提高稳定性
            driver.manage().timeouts().pageLoadTimeout(Duration.ofSeconds(60));
            driver.get(listPageUrl);

            // 找到左侧列表中的所有题目链接
            WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(20));
            
            // 增加多种等待条件，确保页面完全加载
            wait.until(ExpectedConditions.presenceOfElementLocated(By.tagName("body")));
            
            // 尝试等待summary容器加载完成，如果存在的话
            try {
                wait.until(ExpectedConditions.presenceOfElementLocated(By.id("summary")));
                logger.info("找到summary容器");
            } catch (TimeoutException e) {
                logger.warn("未找到summary容器，尝试使用其他选择器");
            }
            
            // 使用多种选择器组合，确保能匹配到所有可能的题目链接
            // 1. 首先尝试主要选择器
            List<WebElement> linkElements = driver.findElements(By.cssSelector("#summary ul.ml-2 li a"));
            
            // 2. 如果主要选择器未找到足够的链接，尝试更通用的选择器
            if (linkElements.isEmpty() || linkElements.size() < 5) {
                logger.info("主要选择器匹配结果不足，尝试更通用的选择器");
                // 尝试多种可能的链接选择器
                List<WebElement> backupLinks1 = driver.findElements(By.cssSelector("a[href*='interview']"));
                List<WebElement> backupLinks2 = driver.findElements(By.cssSelector("#summary a"));
                List<WebElement> backupLinks3 = driver.findElements(By.cssSelector("ul li a"));
                
                // 合并所有找到的链接
                List<WebElement> allLinks = new ArrayList<>();
                allLinks.addAll(linkElements);
                allLinks.addAll(backupLinks1);
                allLinks.addAll(backupLinks2);
                allLinks.addAll(backupLinks3);
                
                // 去重
                linkElements = new ArrayList<>();
                for (WebElement link : allLinks) {
                    String href = link.getAttribute("href");
                    if (href != null && !href.isEmpty() && !linkElements.contains(link)) {
                        linkElements.add(link);
                    }
                }
            }
            
            // 处理所有找到的链接
            for (WebElement link : linkElements) {
                String href = link.getAttribute("href");
                if (href != null && !href.isEmpty()) {
                    // 处理链接格式，有些链接可能包含逗号需要修正
                    if (href.contains(",html")) {
                        href = href.replace(",html", ".html");
                    }
                    // 确保是interview相关的链接
                    if (href.contains("interview")) {
                        // 确保链接不重复
                        if (!questionUrls.contains(href)) {
                            questionUrls.add(href);
                            logger.debug("抓取到题目链接: {}", href);
                        }
                    }
                }
            }
            
            logger.info("列表页抓取到 {} 个题目链接", questionUrls.size());
            
            // 如果还是没找到足够的链接，尝试执行JavaScript来获取所有链接
            if (questionUrls.isEmpty()) {
                logger.info("所有选择器都未找到链接，尝试使用JavaScript获取");
                List<String> jsUrls = (List<String>) ((JavascriptExecutor) driver).executeScript(
                    "return Array.from(document.querySelectorAll('a')).map(a => a.href).filter(href => href && href.includes('interview'));");
                for (String jsUrl : jsUrls) {
                    if (!questionUrls.contains(jsUrl)) {
                        questionUrls.add(jsUrl);
                        logger.debug("JavaScript抓取到题目链接: {}", jsUrl);
                    }
                }
                logger.info("JavaScript额外抓取到 {} 个题目链接", questionUrls.size());
            }
            
        } catch (Exception e) {
            logger.error("抓取列表页题目链接失败: {}", e.getMessage(), e);
        } finally {
            if (driver != null)
                driver.quit();
        }
        return questionUrls;
    }

    /**
     * 抓取单个题目的标题和答案（为兼容StrategySelector而提供）
     */
    public String extractQuestionsFromUrl(String url) {
        return extractQuestionDetail(url);
    }
    
    /**
     * 抓取单个题目的标题和答案
     */
    private String extractQuestionDetail(String url) {
        WebDriver driver = null;
        try {
            WebDriverManager.chromedriver().setup();
            ChromeOptions options = new ChromeOptions();
            String chromePath = detectChromePath();
            if (chromePath != null)
                options.setBinary(chromePath);
            options.addArguments("--headless", "--no-sandbox", "--disable-dev-shm-usage");

            driver = new ChromeDriver(options);
            driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(10));
            // 设置页面加载超时时间为60秒以提高稳定性
            driver.manage().timeouts().pageLoadTimeout(Duration.ofSeconds(60));
            driver.get(url);

            WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(20));
            
            // 先尝试等待页面主体内容加载完成
            wait.until(ExpectedConditions.presenceOfElementLocated(By.tagName("body")));
            
            // 使用更通用的选择器匹配题目标题，增加容错机制
            WebElement titleElem = null;
            String title = "未找到标题";
            String answer = "未找到答案";
            
            try {
                // 尝试多种可能的选择器匹配题目标题
                try {
                    titleElem = wait.until(ExpectedConditions.presenceOfElementLocated(
                            By.cssSelector("h1"))); // 使用更简单的选择器作为主要尝试
                    title = titleElem.getText().trim();
                } catch (TimeoutException e) {
                    // 如果第一种选择器失败，尝试其他可能的选择器
                    logger.warn("主标题选择器失败，尝试备选选择器");
                    // 尝试查找其他可能的标题元素
                    List<WebElement> headers = driver.findElements(By.tagName("h1"));
                    if (!headers.isEmpty()) {
                        titleElem = headers.get(0);
                        title = titleElem.getText().trim();
                    } else {
                        try {
                            // 尝试查找带有特定文本内容的元素
                            WebElement contentElement = driver.findElement(By.xpath("//*[contains(text(), '请列举') or contains(text(), '请解释')]"));
                            title = contentElement.getText().trim();
                        } catch (Exception ex) {
                            logger.warn("无法通过文本内容找到标题，尝试从页面中提取可见文本");
                            // 尝试获取页面中的第一个可见文本块作为标题
                            List<WebElement> textElements = driver.findElements(By.xpath("//body//*[not(self::script) and not(self::style) and normalize-space(text()) != '']"));
                            if (!textElements.isEmpty()) {
                                title = textElements.get(0).getText().trim();
                            }
                        }
                    }
                }
                
                // 匹配答案内容容器，增加容错性
                try {
                    WebElement answerElem = wait.until(ExpectedConditions.presenceOfElementLocated(
                            By.cssSelector("article.markdown-body")));
                    answer = answerElem.getText().trim();
                } catch (TimeoutException e) {
                    logger.warn("答案选择器失败，尝试备选选择器");
                    // 尝试其他可能的答案容器选择器
                    List<WebElement> articleElements = driver.findElements(By.tagName("article"));
                    if (!articleElements.isEmpty()) {
                        answer = articleElements.get(0).getText().trim();
                    } else {
                        // 尝试获取主内容区域
                        WebElement mainContent = driver.findElement(By.tagName("main"));
                        if (mainContent != null) {
                            answer = mainContent.getText().trim();
                        }
                    }
                }
            } catch (Exception e) {
                logger.error("获取标题或答案失败: {}", e.getMessage());
            }

            logger.info("抓取成功: {} 字标题，{} 字答案", title.length(), answer.length());

            return "=== 题目标题 ===\n" + title + "\n\n=== 答案内容 ===\n" + answer + "\n\n";

        } catch (Exception e) {
            logger.error("抓取题目 {} 失败: {}", url, e.getMessage(), e);
            return "抓取失败: " + url + "\n";
        } finally {
            if (driver != null)
                driver.quit();
        }
    }

    /**
     * 抓取整个列表页的所有题目和答案
     * 实现思路：第一步把左边列表抓出来，然后再循环那个列表把内容抓出来
     */
    public String extractAllQuestions(String listPageUrl) {
        logger.info("开始抓取列表页：{}", listPageUrl);
        
        // 第一步：抓取左侧列表中的所有题目链接
        logger.info("第一步：开始提取左侧题目链接列表");
        List<String> urls = extractQuestionUrls(listPageUrl);
        logger.info("成功提取到 {} 个题目链接", urls.size());
        
        // 如果没有提取到链接，返回提示信息
        if (urls.isEmpty()) {
            logger.warn("未能从列表页提取到任何题目链接");
            return "未能从列表页提取到任何题目链接，请检查页面结构是否变化。\n";
        }
        
        // 记录提取到的所有链接，便于调试
        logger.debug("提取到的题目链接列表：{}", urls);
        
        // 第二步：循环处理每个链接，抓取题目内容
        logger.info("第二步：开始循环抓取每个题目的内容");
        StringBuilder sb = new StringBuilder();
        int successCount = 0;
        int failCount = 0;
        
        for (int i = 0; i < urls.size(); i++) {
            String url = urls.get(i);
            logger.info("正在抓取题目 {}/{}: {}", (i + 1), urls.size(), url);
            
            try {
                // 抓取单个题目内容
                String questionContent = extractQuestionDetail(url);
                sb.append(questionContent);
                successCount++;
                logger.info("题目 {}/{} 抓取成功", (i + 1), urls.size());
                
                // 添加延迟，避免对服务器请求过于频繁
                try {
                    logger.debug("添加3秒延迟，避免请求过于频繁");
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    logger.warn("延迟被中断");
                }
                
            } catch (Exception e) {
                failCount++;
                String errorMsg = String.format("题目 {}/{} 抓取失败: {}\n", (i + 1), urls.size(), e.getMessage());
                sb.append(errorMsg);
                logger.error("题目 {}/{} 抓取失败: {}", (i + 1), urls.size(), url, e);
            }
        }
        
        // 添加抓取统计信息
        String summary = String.format("\n=== 抓取统计 ===\n总题目数: %d\n成功抓取: %d\n抓取失败: %d\n成功率: %.1f%%\n\n", 
                urls.size(), successCount, failCount, 
                urls.size() > 0 ? (double)successCount / urls.size() * 100 : 0);
        sb.append(summary);
        
        logger.info(summary);
        return sb.toString();
    }
    
    @Override
    public String extractQuestions(String html) {
        // 简单实现，从HTML中提取文本内容
        return html.replaceAll("<[^>]*>", "").replaceAll("\\s+", " ").trim();
    }
    
    @Override
    public String extractTitle(String html) {
        // 简单实现，从HTML中提取title标签内容
        int startIdx = html.indexOf("<title>");
        int endIdx = html.indexOf("</title>");
        if (startIdx != -1 && endIdx != -1) {
            return html.substring(startIdx + 7, endIdx).trim();
        }
        return "无标题文档";
    }
}
